package at.fhooe.mcm.logic.event;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import at.fhooe.mcm.logic.core.Core;
import at.fhooe.mcm.logic.core.UpdateableService;
import at.fhooe.mcm.logic.logging.ConsoleLoggingService;

public class EventManager implements UpdateableService
{

	public static final String							NAME				= "core.eventmanager";
	public static final String							LOGGING_SROUCE		= "EventManager";

	private Map<EventType, ArrayList<EventListener>>	m_listenerMap		= new HashMap<EventType, ArrayList<EventListener>>();
	private ArrayList<EventListener>					m_handledListeners	= new ArrayList<EventListener>();
	private ArrayList<Event>							m_events1			= new ArrayList<Event>();
	private ArrayList<Event>							m_events2			= new ArrayList<Event>();
	private ArrayList<Event>							m_currentEventList	= m_events1;
	private EventType									m_currentEventType	= null;
	private Core										m_core;

	public static EventManager getInstance(Core _core)
	{
		return (EventManager) _core.getService(NAME);
	}

	public boolean hasListener(EventListener _listener, EventType _eventType)
	{
		ArrayList<EventListener> listeners = m_listenerMap.get(_eventType);

		return listeners == null ? false : listeners.contains(_listener);
	}

	public void addListener(EventListener _listener, EventType _eventType)
	{
		ArrayList<EventListener> listeners = m_listenerMap.get(_eventType);

		if (listeners == null)
		{
			listeners = new ArrayList<EventListener>();
			m_listenerMap.put(_eventType, listeners);
			listeners.add(_listener);
		}
		else if (!listeners.contains(_listener)
				&& !(_eventType.equals(m_currentEventType) && m_handledListeners.contains(_listener)))
		{
			listeners.add(_listener);
		}
		else
		{
			ConsoleLoggingService.getInstance(m_core).logWarning(
					LOGGING_SROUCE,
					"attempt to add listener for event type '" + _eventType.getName() + "' twice ("
							+ _listener.getClass().getName() + ")");
		}
	}

	public void removeListener(EventListener _listener, EventType _eventType)
	{
		ArrayList<EventListener> listeners = m_listenerMap.get(_eventType);

		boolean removed = false;
		if (listeners != null)
		{
			removed = listeners.remove(_listener);
		}

		if (!removed && _eventType.equals(m_currentEventType))
		{
			removed = m_handledListeners.remove(_listener);
		}

		if (!removed)
		{
			ConsoleLoggingService.getInstance(m_core).logWarning(
					LOGGING_SROUCE,
					"attempt to remove unregistered listener for event type '" + _eventType.getName() + "' ("
							+ _listener.getClass().getName() + ")");
		}
	}

	public void removeListener(EventListener _listener)
	{
		Collection<ArrayList<EventListener>> listeners = m_listenerMap.values();

		for (Iterator<ArrayList<EventListener>> it = listeners.iterator(); it.hasNext();)
		{
			it.next().remove(_listener);
		}

		m_handledListeners.remove(_listener);
	}

	public void enqueueEvent(Event _event)
	{
		m_currentEventList.add(_event);
	}

	private void fireEvent(Event _event)
	{
		ArrayList<EventListener> listeners = m_listenerMap.get(_event.getType());

		if (listeners == null)
		{
			return;
		}

		m_currentEventType = _event.getType();

		while (!listeners.isEmpty())
		{
			EventListener listener = listeners.get(listeners.size() - 1);
			m_handledListeners.add(listener);
			listeners.remove(listeners.size() - 1);
			listener.handleEvent(_event);
		}
		m_currentEventType = null;

		listeners.addAll(m_handledListeners);
		m_handledListeners.clear();
	}

	private void swapEventList()
	{
		if (m_currentEventList == m_events1)
		{
			m_currentEventList = m_events2;
		}
		else
		{
			m_currentEventList = m_events1;
		}
	}

	@Override
	public void update()
	{
		List<Event> events = m_currentEventList;
		swapEventList();

		for (Iterator<Event> it = events.iterator(); it.hasNext();)
		{
			fireEvent(it.next());
		}

		events.clear();
	}

	@Override
	public String getName()
	{
		return NAME;
	}

	@Override
	public void initialize(Core _core)
	{
		m_core = _core;
	}

	@Override
	public void shutdown(Core core)
	{
		// TODO Auto-generated method stub
		
	}
}
